#include "databus_appserver.h" 
#include <stdio.h> 
#include <vector>
#include "logger.h"
#include "../core/command_config.h"
#include "../client/client_config.h"
#include "../core/broker_config.h"
#include "AppServer.h"

std::vector<AppServer*> appList;
int appCount=0;
sp_thread_mutex_t mMutex;

JNIEXPORT jstring JNICALL Java_databus_AppServer_GetClazz
  (JNIEnv *env, jobject, jint cmd, jint type)
{
	string clazz=CommandConfig::getInstance().GetClass(cmd);
	jstring className=env->NewStringUTF(clazz.c_str());
	return className;
}

/*
 * Class:     databus_AppServer
 * Method:    GetCommand
 * Signature: (Ljava/lang/String;II)I
 */
JNIEXPORT jint JNICALL Java_databus_AppServer_GetCommand
  (JNIEnv *env, jobject, jstring clazz, jint type, jint appId)
{
	const char* className=env->GetStringUTFChars(clazz,NULL);
	if(className==NULL)
		return 0;
	int cmd=0;
	try{
	    cmd=CommandConfig::getInstance().GetCommand(className);
	}
	catch(char* err)
	{
		LOG4_ERROR("Class type to command failed,msg:%s",err);
	}
	return cmd;
}

JNIEXPORT jint JNICALL Java_databus_AppServer_CreateServer
  (JNIEnv *, jobject)
{
	if(appList.size()==0)
	{
		appList.resize(5);
		for(int i=0;i<5;i++)
			appList[i]=NULL;
	}
	if(appCount>=appList.size())
		return -1;
	jint ret=appCount;
	appCount++;
	return ret;
}

JNIEXPORT jint JNICALL Java_databus_AppServer_Initialize
  (JNIEnv *env, jobject thisObj,jint index ,jstring ConfigPath,jstring ConfigFile)
{
	if(index<0 || index>=appCount)
		return -1;
	const char* configpath=env->GetStringUTFChars(ConfigPath,NULL);
	const char* configfile=env->GetStringUTFChars(ConfigFile,NULL);
	bool hasConfigPath=false;
	if(configpath!=NULL && strlen(configpath)>0)
		hasConfigPath=true;
	if(hasConfigPath)
	    printf("config path:%s\r\n",configpath);
	if(configfile!=NULL && strlen(configfile)>0) 
		printf("config file:%s\r\n",configfile);
#ifdef _WIN32
	
	if(hasConfigPath)
	{
		string log_file;
		string tmp(configpath);
		if(tmp.at(tmp.size()-1)=='\\' || tmp.at(tmp.size()-1)=='/')
			log_file=tmp+"log4cxx.properties";
		else
		    log_file=tmp+"\\log4cxx.properties";
		LogInit(log_file.c_str());
	}
	else
	{
		WCHAR oldPath[1024]={0};
	    GetCurrentDirectory(1024,oldPath);
	    wprintf(L"old path:%s\r\n",oldPath);
		wstring log_file(oldPath);
		log_file+=L"\\..\\config\\log4cxx.properties";
		LogInit(log_file.c_str());
	}
	
#elif __linux__
	string log_file;
	if(hasConfigPath)
	{
		string tmp(configpath);
		if(tmp.at(tmp.size()-1)=='\\' || tmp.at(tmp.size()-1)=='/')
			log_file=tmp+"log4cxx.properties";
		else
		    log_file=tmp+"/log4cxx.properties";
	}
	else
	{
	    log_file = "log4cxx.properties";
	}
	LogInit(log_file.c_str());
#endif
	
	if(hasConfigPath)
		XMLConfig::SetPath(configpath);
	AppServer* app=new AppServer();
	appList[index]=app;

	CommandConfig::getInstance();
	string xml_file = configfile;
	LOG4_INFO("config file:%s\r\n",xml_file.c_str());

	ClientConfig cfg;
	if(!cfg.LoadFile(xml_file))
	{   
		LOG4_ERROR("read client xml config file error,xml:%s",xml_file.c_str());
		return -1;
	}   
	else
		LOG4_INFO("load xml config file success,xml:%s",xml_file.c_str());
	return app->Initialize(&cfg.clientCfg,NULL);
}
JNIEXPORT jboolean JNICALL Java_databus_AppServer_Release
  (JNIEnv *, jobject,jint index )
{
	if(index<0 || index>=appCount)
		return false;
	if(appList[index])
	{
		appList[index]->Stop();
		return appList[index]->Release();
	}
	return false;
}


void GetPackageInfo(JNIEnv *env,jobject obj,int& type,int& cmd,unsigned int& serail,int& multicast,std::string& data,std::string& ext)
{
	jclass cls=env->GetObjectClass(obj); 
	jobject header=env->GetObjectField(obj,env-> GetFieldID(cls,"header","Ldatabus/PackageHeader;"));
	jobject body=env->GetObjectField(obj,env-> GetFieldID(cls,"data","[B"));
	jobject extData=env->GetObjectField(obj,env-> GetFieldID(cls,"extData","[B"));

	jclass headerCls=env->GetObjectClass(header); 
	type=env->GetIntField(header,env-> GetFieldID(headerCls,"type","I"));
	cmd=env->GetIntField(header,env-> GetFieldID(headerCls,"command","I"));
	serail=env->GetIntField(header,env-> GetFieldID(headerCls,"serialNum","I"));
	multicast=env->GetIntField(header,env-> GetFieldID(headerCls,"multicast","I"));
	int size=env->GetArrayLength((jbyteArray)body);
	data.resize(size);
	env->GetByteArrayRegion((jbyteArray)body,0,size,(jbyte*)data.data());
	if(extData)
	{
		size=env->GetArrayLength((jbyteArray)extData);
	    ext.resize(size);
	    env->GetByteArrayRegion((jbyteArray)extData,0,size,(jbyte*)ext.data());
	}
}

JNIEXPORT jboolean JNICALL Java_databus_AppServer_PostMsg
  (JNIEnv *env, jobject thisObj,jint index , jobject obj)
{
	if(index<0 || index>=appCount)
	{
		LOG4_ERROR("error index:%d",index);
		return false;
	}
	int type;
	int cmd;
	std::string data;
	std::string ext;
	unsigned int serial=0;
	int multicast=0;

	GetPackageInfo(env,obj,type,cmd,serial,multicast,data,ext);
	Options opt;
	opt.type=(PackageHeader::MsgType)type;
	opt.serial=serial;
	if(multicast!=0)
	{
		//LOG4_INFO("multicast:%d",multicast);
	    opt.multicast=true;
	}
	if(ext.size()>0)
	{
		opt.ext=true;
		ext.append(data);
		serial=appList[index]->PostMessage(cmd,ext.data(),ext.size(),opt);
	}
	else
	    serial=appList[index]->PostMessage(cmd,data.data(),data.size(),opt);

	jclass cls=env->GetObjectClass(obj); 
	jobject header=env->GetObjectField(obj,env-> GetFieldID(cls,"header","Ldatabus/PackageHeader;"));
	jclass headerCls=env->GetObjectClass(header); 
	env->SetIntField(header,env-> GetFieldID(headerCls,"serialNum","I"),serial);
	return serial!=0;
}
/*
 * Class:     databus_AppServer
 * Method:    Reply
 * Signature: (Ldatabus/PackageHeader;[B)Z
 */
JNIEXPORT jboolean JNICALL Java_databus_AppServer_Reply
  (JNIEnv *env, jobject,jint index , jobject reqHeader, jbyteArray data)
{
	if(index<0 || index>=appCount)
		return false;
	jsize len=env->GetArrayLength(data);
	char* buf=new char[len];
	env->GetByteArrayRegion(data,0,len,(jbyte*)buf);

	jclass headerCls=env->GetObjectClass(reqHeader); 
	PackageHeader header;
	header.set_version(env->GetIntField(reqHeader,env-> GetFieldID(headerCls,"version","I")));
	header.set_command(env->GetIntField(reqHeader, env->GetFieldID(headerCls, "command", "I")));
	header.set_type(env->GetIntField(reqHeader, env->GetFieldID(headerCls, "type", "I")));
	header.set_serialnum(env->GetIntField(reqHeader, env->GetFieldID(headerCls, "serialNum", "I")));
	header.set_srcaddr(env->GetIntField(reqHeader, env->GetFieldID(headerCls, "srcaddr", "I")));
	header.set_dstaddr(env->GetIntField(reqHeader, env->GetFieldID(headerCls, "dstaddr", "I")));
	bool ret=appList[index]->Reply(header,buf,len);
	delete []buf;
	return ret;
}
JNIEXPORT jboolean JNICALL Java_databus_AppServer_SendMsg
  (JNIEnv *env, jobject,jint index , jobject req, jobject resp, jint msTimeOut)
{
	if(index<0 || index>=appCount)
		return false;
	int type;
	int cmd;
	std::string data;
	std::string ext;
	unsigned int serial=0;
	int multicast=0;

	GetPackageInfo(env,req,type,cmd,serial,multicast,data,ext);
	std::string retData;
	PackageHeader header;
	bool ok=appList[index]->SendMessage(cmd,data.data(),data.size(),header,retData,msTimeOut);
	//LOG4_INFO("header:%s",header.DebugString().c_str());
	if(!ok)
	{
		LOG4_ERROR("SendMessage failed,cmd=%d",cmd);
	    return false;
	}
	jclass cls=env->GetObjectClass(resp); 
	jobject h=env->GetObjectField(resp,env-> GetFieldID(cls,"header","Ldatabus/PackageHeader;"));

	jclass headerCls=env->GetObjectClass(h); 
	env->SetIntField(h,env-> GetFieldID(headerCls,"version","I"),header.version());
	env->SetIntField(h, env->GetFieldID(headerCls, "type", "I"), header.type());
	env->SetIntField(h, env->GetFieldID(headerCls, "serialnum", "I"), header.serialnum());
	env->SetIntField(h, env->GetFieldID(headerCls, "command", "I"), header.command());
	env->SetIntField(h, env->GetFieldID(headerCls, "srcaddr", "I"), header.srcaddr());
	env->SetIntField(h, env->GetFieldID(headerCls, "dstaddr", "I"), header.dstaddr());
	if(header.hasext())
	{
		unsigned short extSize=*((unsigned short*)retData.data());
		extSize=ntohs(extSize);
		jbyteArray extData=env->NewByteArray(extSize);
	    env->SetByteArrayRegion(extData,0,extSize,(jbyte*)retData.data());
		env->SetObjectField(resp,env-> GetFieldID(cls,"extData","[B"),extData);

		jbyteArray body=env->NewByteArray(retData.size()-extSize);
	    env->SetByteArrayRegion(body,0,retData.size()-extSize,(jbyte*)retData.data()+extSize);
		env->SetObjectField(resp,env-> GetFieldID(cls,"data","[B"),body);
	}
	else
	{
		jbyteArray body=env->NewByteArray(retData.size());
	    env->SetByteArrayRegion(body,0,retData.size(),(jbyte*)retData.data());
	    env->SetObjectField(resp,env-> GetFieldID(cls,"data","[B"),body);
	}
	return ok;
}

JNIEXPORT jboolean JNICALL Java_databus_AppServer_QueryData
  (JNIEnv *env, jobject,jint index , jobject req, jobject resp)
{
	if(index<0 || index>=appCount)
		return false;
	int type;
	int cmd;
	std::string data;
	std::string ext;
	unsigned int serial=0;
	int multicast=0;

	GetPackageInfo(env,req,type,cmd,serial,multicast,data,ext);
	/*IDataManager* manager=appList[index]->GetDataManager();
	Storage::QueryDataRequest request;
	if(!manager)
	{
		LOG4_ERROR("QueryData failed, no data manager.");
		return false;
	}
	if(!request.ParseFromString(data))
	{
		LOG4_ERROR("QueryData failed, farse request failed.");
		return false;
	}
	vector<PublishPtr> dataset;
	std::string err;
	if(manager->QueryData(&request,err,dataset)!=0)
	{
		LOG4_ERROR("QueryData failed,err:%s",err.c_str());
		return false;
	}
	Storage::QueryDataResponse response;
	for(int i=0;i<dataset.size();i++)
	{
		MsgExpress::PublishData* data=response.add_data();
		data->CopyFrom(*dataset.at(i).get());
	}
	response.set_result(0);
	response.set_msg("");
	std::string retData=response.SerializeAsString();
	PackageHeader header;
	
	jclass cls=env->GetObjectClass(resp); 

	{
		jbyteArray body=env->NewByteArray(retData.size());
	    env->SetByteArrayRegion(body,0,retData.size(),(jbyte*)retData.data());
	    env->SetObjectField(resp,env-> GetFieldID(cls,"data","[B"),body);
	}*/
	return true;
}

JNIEXPORT jboolean JNICALL Java_databus_AppServer_GetPackage
  (JNIEnv *env, jobject,jint index , jobject obj)
{
	if(index<0 || index>=appCount)
		return false;
	MessageItem* item=appList[index]->GetItem();
	if(item==NULL)
		return false;
	
	jclass cls=env->GetObjectClass(obj); 
	jobject header=env->GetObjectField(obj,env-> GetFieldID(cls,"header","Ldatabus/PackageHeader;"));

	jclass headerCls=env->GetObjectClass(header); 
	env->SetIntField(header,env-> GetFieldID(headerCls,"version","I"),item->header.version());
	env->SetIntField(header,env-> GetFieldID(headerCls,"type","I"),item->header.type());
	env->SetIntField(header, env->GetFieldID(headerCls, "serialnum", "I"), item->header.serialnum());
	env->SetIntField(header,env-> GetFieldID(headerCls,"command","I"),item->header.command());
	env->SetIntField(header,env-> GetFieldID(headerCls,"srcaddr","I"),item->header.srcaddr());
	env->SetIntField(header,env-> GetFieldID(headerCls,"dstaddr","I"),item->header.dstaddr());
	if(item->header.hasext())
	{
		unsigned short extSize=*((unsigned short*)item->body.data());
		extSize=ntohs(extSize);
		jbyteArray extData=env->NewByteArray(extSize);
	    env->SetByteArrayRegion(extData,0,extSize,(jbyte*)item->body.data());
		env->SetObjectField(obj,env-> GetFieldID(cls,"extData","[B"),extData);

		jbyteArray body=env->NewByteArray(item->body.size()-extSize);
	    env->SetByteArrayRegion(body,0,item->body.size()-extSize,(jbyte*)item->body.data()+extSize);
		env->SetObjectField(obj,env-> GetFieldID(cls,"data","[B"),body);
	}
	else
	{
		jbyteArray body=env->NewByteArray(item->body.size());
	    env->SetByteArrayRegion(body,0,item->body.size(),(jbyte*)item->body.data());
	    env->SetObjectField(obj,env-> GetFieldID(cls,"data","[B"),body);
	}
	delete item;
	return true;
}

JNIEXPORT void JNICALL Java_databus_AppServer_GetLastError
  (JNIEnv *env, jobject,jint index , jobject obj)
{
	if(index<0 || index>=appCount)
		return;
	jclass cls=env->GetObjectClass(obj); 
	MsgExpress::ErrMessage err=appList[index]->GetLatestError();
	env->SetIntField(obj,env-> GetFieldID(cls,"errcode","I"),err.errcode());
	jstring errmsg=env->NewStringUTF(err.errmsg().c_str());
	env->SetObjectField(obj,env-> GetFieldID(cls,"errmsg","Ljava/lang/String;"),errmsg);
}
